import { AlignType } from "@/Constants";
import { IPoint, ISize } from "@/Interface";

const PADDING = 30;
let easyMode = false;

let startInfo: ISize & IPoint;  // x,y是包围盒左上角
let endInfo: ISize & IPoint;

// 计算所有可能经过的点
export const computedProbablyPoints = (startDir: AlignType, sInfo: ISize & IPoint, endDir: AlignType, eInfo: ISize & IPoint) => {
  startInfo = sInfo;
  endInfo = eInfo;

  // 设置起终点坐标
  const [startPoint, endPoint] = getEdgePos(startDir, sInfo, endDir, eInfo);
  // 保存所有可能经过的点
  let points: IPoint[] = [];

  // 宽松模式则把真正的起点和终点加入点列表中
  // if (easy) {
  points.push(startPoint, endPoint);
  // }

  // 伪起点：经过起点且垂直于起点所在边的线与包围框线的交点
  let fakeStartPoint = findStartNextOrEndPrePoint(sInfo, startPoint) as IPoint;
  points.push(fakeStartPoint);

  // 伪终点：经过终点且垂直于终点所在边的线与包围框线的交点
  let fakeEndPoint = findStartNextOrEndPrePoint(eInfo, endPoint) as IPoint;
  points.push(fakeEndPoint);

  // 经过起点且垂直于起点所在边的线 与 经过终点且垂直于终点所在边的线 的交点
  let startEndPointVerticalLineIntersection = getIntersection(
    [startPoint, fakeStartPoint],
    [endPoint, fakeEndPoint]
  );
  startEndPointVerticalLineIntersection &&
    points.push(startEndPointVerticalLineIntersection);

  // 当 经过起点且垂直于起点所在边的线 与 经过终点且垂直于终点所在边的线 平行时，计算一条垂直线与经过另一个点的伪点的水平线 的节点
  if (!startEndPointVerticalLineIntersection) {
    let p1 = getIntersection(
      [startPoint, fakeStartPoint], // 假设经过起点的垂直线是垂直的
      [fakeEndPoint, { x: fakeEndPoint.x + 10, y: fakeEndPoint.y }] // 那么就要计算经过伪终点的水平线。水平线上的点y坐标相同，所以x坐标随便加减多少数值都可以
    );
    p1 && points.push(p1);
    let p2 = getIntersection(
      [startPoint, fakeStartPoint], // 假设经过起点的垂直线是水平的
      [fakeEndPoint, { x: fakeEndPoint.x, y: fakeEndPoint.y + 10 }] // 那么就要计算经过伪终点的垂直线。
    );
    p2 && points.push(p2);
    // 下面同上
    let p3 = getIntersection(
      [endPoint, fakeEndPoint],
      [fakeStartPoint, { x: fakeStartPoint.x + 10, y: fakeStartPoint.y }]
    );
    p3 && points.push(p3);
    let p4 = getIntersection(
      [endPoint, fakeEndPoint],
      [fakeStartPoint, { x: fakeStartPoint.x, y: fakeStartPoint.y + 10 }]
    );
    p4 && points.push(p4);
  }

  // 伪起点和伪终点形成的矩形 和 起点元素包围框 组成一个大矩形 的四个顶点
  points.push(
    ...getBoundingBox([
      // 伪起点终点
      fakeStartPoint,
      fakeEndPoint,
      // 起点元素包围框
      { x: startInfo.x - PADDING, y: startInfo.y - PADDING }, // 左上顶点
      { x: startInfo.x + startInfo.width + PADDING, y: startInfo.y + startInfo.height + PADDING }, // 右下顶点
    ])
  );

  // 伪起点和伪终点形成的矩形 和 终点元素包围框 组成一个大矩形 的四个顶点
  points.push(
    ...getBoundingBox([
      // 伪起点终点
      fakeStartPoint,
      fakeEndPoint,
      // 终点元素包围框
      { x: endInfo.x - PADDING, y: endInfo.y - PADDING }, // 左上顶点
      { x: endInfo.x + endInfo.width + PADDING, y: endInfo.y + endInfo.height + PADDING }, // 右下顶点
    ])
  );

  // 去重
  points = removeDuplicatePoint(points);   // 9个点

  return {
    startPoint,
    endPoint,
    fakeStartPoint,
    fakeEndPoint,
    points,
  };
};

// 设置起终点坐标
export const getEdgePos = (startDir: AlignType, startInfo: ISize & IPoint, endDir: AlignType, endInfo: ISize & IPoint) => {
  // 起终点
  let startPoint: IPoint = { x: 0, y: 0 };
  let endPoint: IPoint = { x: 0, y: 0 };
  switch (startDir) {
    case AlignType.LEFT:
      startPoint = { x: startInfo.x, y: startInfo.y + startInfo.height / 2 }; // 左
      break;
    case AlignType.TOP:
      startPoint = { x: startInfo.x + startInfo.width / 2, y: startInfo.y }; // 上
      break;
    case AlignType.RIGHT:
      startPoint = { x: startInfo.x + startInfo.width, y: startInfo.y + startInfo.height / 2 }; // 右
      break;
    case AlignType.BOTTOM:
      startPoint = { x: startInfo.x + startInfo.width / 2, y: startInfo.y + startInfo.height }; // 下
      break;
    default:
      break;
  }

  switch (endDir) {
    case AlignType.LEFT:
      endPoint = { x: endInfo.x, y: endInfo.y + endInfo.height / 2 }; // 左
      break;
    case AlignType.TOP:
      endPoint = { x: endInfo.x + endInfo.width / 2, y: endInfo.y }; // 上
      break;
    case AlignType.RIGHT:
      endPoint = { x: endInfo.x + endInfo.width, y: endInfo.y + endInfo.height / 2 }; // 右
      break;
    case AlignType.BOTTOM:
      endPoint = { x: endInfo.x + endInfo.width / 2, y: endInfo.y + endInfo.height }; // 下
      break;
    default:
      break;
  }
  return [startPoint, endPoint]
};

// 检查一个点是否在一条线段上
export const checkPointIsInSegment = (point: IPoint, seg: IPoint[]) => {
  if (point.x === seg[0].x) {
    if (
      point.y >= Math.min(seg[0].y, seg[1].y) &&
      point.y <= Math.max(seg[0].y, seg[1].y)
    ) {
      return true;
    }
  } else if (point.y === seg[0].y) {
    if (
      point.x >= Math.min(seg[0].x, seg[1].x) &&
      point.x <= Math.max(seg[0].x, seg[1].x)
    ) {
      return true;
    }
  }
  return false;
};

// 计算两条线段的交点
export const getIntersection = (seg1: IPoint[], seg2: IPoint[]) => {
  // 两条垂直线不会相交
  if (seg1[0].x === seg1[1].x && seg2[0].x === seg2[1].x) {
    return null;
  }
  // 两条水平线不会相交
  if (seg1[0].y === seg1[1].y && seg2[0].y === seg2[1].y) {
    return null;
  }
  // seg1是水平线、seg2是垂直线
  if (seg1[0].y === seg1[1].y && seg2[0].x === seg2[1].x) {
    return { x: seg2[0].x, y: seg1[0].y };
  }
  // seg1是垂直线、seg2是水平线
  if (seg1[0].x === seg1[1].x && seg2[0].y === seg2[1].y) {
    return { x: seg1[0].x, y: seg2[0].y };
  }
};

// 找出起点的下一个点或终点的前一个点
export const findStartNextOrEndPrePoint = (rectInfo: ISize & IPoint, point: IPoint) => {
  // 起点或终点在左边
  if (point.x === rectInfo.x) {
    return { x: rectInfo.x - PADDING, y: rectInfo.y + rectInfo.height / 2 };
  } else if (point.y === rectInfo.y) {
    // 起点或终点在上边
    return { x: rectInfo.x + rectInfo.width / 2, y: rectInfo.y - PADDING };
  } else if (point.x === rectInfo.x + rectInfo.width) {
    // 起点或终点在右边
    return {
      x: rectInfo.x + rectInfo.width + PADDING,
      y: rectInfo.y + rectInfo.height / 2,
    };
  } else if (point.y === rectInfo.y + rectInfo.height) {
    // 起点或终点在下边
    return {
      x: rectInfo.x + rectInfo.width / 2,
      y: rectInfo.y + rectInfo.height + PADDING,
    };
  }
};

// 计算出给定点可以形成的最大的矩形的四个顶点
export const getBoundingBox = (points: IPoint[]) => {
  let boundingBoxXList: number[] = [];
  let boundingBoxYList: number[] = [];
  points.forEach((item) => {
    boundingBoxXList.push(item.x);
    boundingBoxYList.push(item.y);
  });
  let minBoundingBoxX = Math.min(...boundingBoxXList);
  let minBoundingBoxY = Math.min(...boundingBoxYList);
  let maxBoundingBoxX = Math.max(...boundingBoxXList);
  let maxBoundingBoxY = Math.max(...boundingBoxYList);
  return [
    { x: minBoundingBoxX, y: minBoundingBoxY },
    { x: maxBoundingBoxX, y: minBoundingBoxY },
    { x: minBoundingBoxX, y: maxBoundingBoxY },
    { x: maxBoundingBoxX, y: maxBoundingBoxY },
  ];
};

// 找出一个点周边的点
export const getNextPoints = (point: IPoint, points: IPoint[]) => {
  let { x, y } = point;
  let xSamePoints: IPoint[] = [];
  let ySamePoints: IPoint[] = [];

  // 找出x或y坐标相同的点
  points.forEach((item) => {
    // 跳过目标点
    if (checkIsSamePoint(point, item)) {
      return;
    }
    if (item.x === x) {
      xSamePoints.push(item);
    }
    if (item.y === y) {
      ySamePoints.push(item);
    }
  });

  // 找出x方向最近的点
  let xNextPoints = getNextPoint(x, y, ySamePoints, "x");

  // 找出y方向最近的点
  let yNextPoints = getNextPoint(x, y, xSamePoints, "y");

  return [...xNextPoints, ...yNextPoints];
};

// 找出水平或垂直方向上最近的点
export const getNextPoint = (x: number, y: number, points: IPoint[], dir: string) => {
  // let index = dir === "x" ? 'x' : 'y'; // 求水平方向上最近的点，那么它们y坐标都是相同的，要比较x坐标，反之亦然
  let value = dir === "x" ? x : y;
  let nextLeftTopPoint: IPoint | null = null;
  let nextRIghtBottomPoint: IPoint | null = null;

  for (let i = 0; i < points.length; i++) {
    let cur = points[i] as IPoint;
    // 检查当前点和目标点的连线是否穿过起终点元素，宽松模式下直接跳过该检测
    if (!easyMode && checkLineThroughElements({ x, y }, cur)) {
      continue;
    }
    // 左侧或上方最近的点
    if (cur[dir] < value) {
      if (nextLeftTopPoint) {
        if (cur[dir] > nextLeftTopPoint[dir]) {
          nextLeftTopPoint = cur;
        }
      } else {
        nextLeftTopPoint = cur;
      }
    }
    // 右侧或下方最近的点
    if (cur[dir] > value) {
      if (nextRIghtBottomPoint) {
        if (cur[dir] < nextRIghtBottomPoint[dir]) {
          nextRIghtBottomPoint = cur;
        }
      } else {
        nextRIghtBottomPoint = cur;
      }
    }
  }

  return [nextLeftTopPoint, nextRIghtBottomPoint].filter((point) => {
    return !!point;
  });
};

// 检查两个点组成的线段是否穿过起终点元素
export const checkLineThroughElements = (a: IPoint, b: IPoint, rects: (ISize & IPoint)[] = [startInfo, endInfo]) => {
  let minX = Math.min(a.x, b.x);
  let maxX = Math.max(a.x, b.x);
  let minY = Math.min(a.y, b.y);
  let maxY = Math.max(a.y, b.y);
  // 水平线
  if (a.y === b.y) {
    for (let i = 0; i < rects.length; i++) {
      let rect = rects[i];
      if (
        minY > rect.y - PADDING &&
        minY < rect.y + rect.height + PADDING &&
        minX < rect.x + rect.width + PADDING &&
        maxX > rect.x - PADDING
      ) {
        return true;
      }
    }
  } else if (a.x === b.x) {
    // 垂直线
    for (let i = 0; i < rects.length; i++) {
      let rect = rects[i];
      if (
        minX > rect.x - PADDING &&
        minX < rect.x + rect.width + PADDING &&
        minY < rect.y + rect.height + PADDING &&
        maxY > rect.y - PADDING
      ) {
        return true;
      }
    }
  }

  return false;
};

// 检测是否为同一个点
export const checkIsSamePoint = (a: IPoint, b: IPoint) => {
  return a.x === b.x && a.y === b.y;
};

// 去重
export const removeDuplicatePoint = (points: IPoint[]) => {
  let res: IPoint[] = [];
  let cache: { [key: string]: boolean } = {};
  points.forEach(({ x, y }) => {
    if (cache[x + "-" + y]) {
      return;
    } else {
      cache[x + "-" + y] = true;
      res.push({ x, y });
    }
  });
  return res;
};
